#define CROW_ENABLE_LOGGING
#include "crow_all.h"
#include <fstream>
#include <sstream>
#include <shared_mutex>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <unordered_set>
#include <cstdlib>
#include <cstdint>

// Global synchronization for concurrent requests
static std::shared_mutex g_dbMutex;
static const char* kDbPath = "/db.json";
static const char* kEnvPath = "/.env";
// No global cached auth; read .env each request to allow dynamic updates

static crow::LogLevel getLogLevelFromEnv()
{
	const char* v = std::getenv("CROW_LOG_LEVEL");
	if (!v) return crow::LogLevel::Info;
	std::string s(v);
	for (auto& ch : s) ch = (char)toupper((unsigned char)ch);
	if (s == "DEBUG") return crow::LogLevel::Debug;
	if (s == "INFO") return crow::LogLevel::Info;
	if (s == "WARNING" || s == "WARN") return crow::LogLevel::Warning;
	if (s == "ERROR" || s == "ERR") return crow::LogLevel::Error;
	return crow::LogLevel::Info;
}

static std::string readWholeFile(const char* path)
{
	std::ifstream in(path, std::ios::in | std::ios::binary);
	if (!in)
	{
		return std::string();
	}
	std::ostringstream ss;
	ss << in.rdbuf();
	return ss.str();
}

static bool writeFileAtomically(const char* path, const std::string& content)
{
	std::string tmp = std::string(path) + ".tmp";
	bool wroteTmp = false;
	{
		std::ofstream out(tmp.c_str(), std::ios::out | std::ios::binary | std::ios::trunc);
		if (out)
		{
			out.write(content.data(), static_cast<std::streamsize>(content.size()));
			if (out)
			{
				out.flush();
				wroteTmp = true;
			}
		}
	}
	if (wroteTmp)
	{
		if (0 == std::rename(tmp.c_str(), path))
		{
			return true;
		}
		CROW_LOG_WARNING << "[IO] rename failed for " << path << ": " << std::strerror(errno) << " (" << errno << ") - fallback to direct write";
		std::remove(tmp.c_str());
	}
	else
	{
		CROW_LOG_WARNING << "[IO] write tmp failed for " << path << ", fallback to direct write";
	}
	// fallback: direct truncating write
	std::ofstream out2(path, std::ios::out | std::ios::binary | std::ios::trunc);
	if (!out2) return false;
	out2.write(content.data(), static_cast<std::streamsize>(content.size()));
	if (!out2) return false;
	out2.flush();
	return true;
}

static std::string trim(const std::string& s)
{
	std::string::size_type start = 0;
	while (start < s.size() && (s[start] == ' ' || s[start] == '\n' || s[start] == '\r' || s[start] == '\t')) ++start;
	std::string::size_type end = s.size();
	while (end > start && (s[end - 1] == ' ' || s[end - 1] == '\n' || s[end - 1] == '\r' || s[end - 1] == '\t')) --end;
	return s.substr(start, end - start);
}

static crow::response jsonCode(int httpCode, int code)
{
	crow::json::wvalue w;
	w["code"] = code;
	crow::response resp;
	resp.code = httpCode;
	resp.add_header("Content-Type", "application/json");
	resp.write(w.dump());
	return resp;
}

static crow::response jsonCode(int httpCode, int code, const std::string& msg)
{
	crow::json::wvalue w;
	w["code"] = code;
	w["msg"] = msg;
	crow::response resp;
	resp.code = httpCode;
	resp.add_header("Content-Type", "application/json");
	resp.write(w.dump());
	return resp;
}

static std::string base64Encode(const std::string& input)
{
	static const char* kAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
	const size_t n = input.size();
	const size_t outLen = ((n + 2) / 3) * 4;
	std::string out(outLen, '\0');

	size_t i = 0;
	size_t o = 0;

	// 处理完整 3 字节块
	while (i + 2 < n)
	{
		uint32_t triad = (static_cast<uint32_t>(static_cast<uint8_t>(input[i])) << 16)
			| (static_cast<uint32_t>(static_cast<uint8_t>(input[i + 1])) << 8)
			| static_cast<uint32_t>(static_cast<uint8_t>(input[i + 2]));
		i += 3;

		out[o++] = kAlphabet[(triad >> 18) & 0x3F];
		out[o++] = kAlphabet[(triad >> 12) & 0x3F];
		out[o++] = kAlphabet[(triad >> 6) & 0x3F];
		out[o++] = kAlphabet[triad & 0x3F];
	}

	// 处理尾部 1 或 2 字节
	if (i < n)
	{
		uint8_t b0 = static_cast<uint8_t>(input[i++]);
		out[o++] = kAlphabet[(b0 >> 2) & 0x3F];
		if (i == n)
		{
			out[o++] = kAlphabet[(b0 & 0x03) << 4];
			out[o++] = '=';
			out[o++] = '=';
		}
		else
		{
			uint8_t b1 = static_cast<uint8_t>(input[i++]);
			out[o++] = kAlphabet[((b0 & 0x03) << 4) | (b1 >> 4)];
			out[o++] = kAlphabet[(b1 & 0x0F) << 2];
			out[o++] = '=';
		}
	}
	return out;
}

static std::unordered_set<std::string> loadAuthHeaders()
{
	std::unordered_set<std::string> headers;
	std::string file = readWholeFile(kEnvPath);
	if (file.empty()) return headers;
	std::string line;
	line.reserve(128);
	for (size_t i = 0; i <= file.size(); ++i)
	{
		char c = (i < file.size() ? file[i] : '\n');
		if (c == '\n' || c == '\r')
		{
			std::string token = trim(line);
			if (!token.empty())
			{
				// Convert plain text "username:password" to base64
				std::string base64Token = base64Encode(token);
				if (!base64Token.empty())
				{
					headers.insert(std::string("Basic ") + base64Token);
				}
			}
			line.clear();
			continue;
		}
		line.push_back(c);
	}
	return headers;
}

static bool isAuthorized(const crow::request& req)
{
	auto it = req.headers.find("Authorization");
	if (it == req.headers.end()) return false;
	const std::string& authHeader = it->second;
	
	auto headers = loadAuthHeaders();
	if (headers.empty()) return false;
	return headers.find(authHeader) != headers.end();
}

static bool isAllDigits(const std::string& s)
{
	if (s.empty()) return false;
	for (char c : s) { if (c < '0' || c > '9') return false; }
	return true;
}

static bool isCollectionObject(const crow::json::rvalue& v)
{
	if (v.t() != crow::json::type::Object) return false;
	for (const auto& kv : v)
	{
		if (!isAllDigits(kv.key())) return false;
	}
	return true;
}

static std::string buildArrayJsonStringFromObjectMap(const crow::json::rvalue& obj)
{
	std::string out;
	out.reserve(256);
	out.push_back('[');
	bool first = true;
	for (const auto& kv : obj)
	{
		if (!first) out.push_back(',');
		first = false;
		// ensure element has id field matching key, if it's an object
		if (kv.t() == crow::json::type::Object)
		{
			crow::json::wvalue elem;
			for (const auto& f : kv) elem[f.key()] = f;
			// set id
			elem["id"] = std::stoi(kv.key());
			out += elem.dump();
		}
		else
		{
			out += crow::json::wvalue(kv).dump();
		}
	}
	out.push_back(']');
	return out;
}

static int computeNextId(const crow::json::rvalue& obj)
{
	int mx = 0;
	for (const auto& kv : obj)
	{
		if (isAllDigits(kv.key()))
		{
			int v = std::stoi(kv.key());
			if (v > mx) mx = v;
		}
	}
	return mx + 1;
}

// Merge two JSON values (shallow for objects, append for arrays)
static crow::json::wvalue mergeJson(const crow::json::rvalue& base, const crow::json::rvalue& patch)
{
	if (base.t() == crow::json::type::Object && patch.t() == crow::json::type::Object)
	{
		crow::json::wvalue out;
		for (const auto& kv : base)
		{
			out[kv.key()] = kv;
		}
		for (const auto& kv : patch)
		{
			out[kv.key()] = kv;
		}
		return out;
	}
	// For non-object types, replace with patch
	return crow::json::wvalue(patch);
}

// Basic Auth middleware
struct BasicAuth
{
	struct context {};
	void before_handle(crow::request& req, crow::response& res, context&)
	{
		// Expect header: Authorization: Basic <base64>
		if (!isAuthorized(req))
		{
			res.code = 401;
			res.add_header("WWW-Authenticate", "Basic realm=\\\"tiny-json-rest\\\"");
			res.add_header("Content-Type", "application/json");
			crow::json::wvalue w;
			w["code"] = 1;
			w["msg"] = "未授权";
			res.write(w.dump());
			res.end();
			return;
		}
	}
	void after_handle(crow::request&, crow::response&, context&) {}
};

int main()
{
	crow::logger::setLogLevel(getLogLevelFromEnv());
	// Log allowed Basic tokens at startup
	auto allowed = loadAuthHeaders();
	if (allowed.empty())
	{
		CROW_LOG_INFO << "[Auth] No credentials found in /.env";
	}
	else
	{
		CROW_LOG_INFO << "[Auth] Allowed Basic tokens:";
		for (const auto& h : allowed)
		{
			CROW_LOG_INFO << "  " << h;
		}
	}

	crow::App<BasicAuth> app;

	// GET / -> return full JSON
	CROW_ROUTE(app, "/").methods(crow::HTTPMethod::GET)([](const crow::request&)
	{
		std::shared_lock<std::shared_mutex> lock(g_dbMutex);
		std::string body = readWholeFile(kDbPath);
		if (body.empty())
		{
			body = "{}";
		}
		crow::response resp;
		resp.code = 200;
		resp.add_header("Content-Type", "application/json");
		resp.write(body);
		return resp;
	});

	// PUT / -> replace JSON document with request body
	CROW_ROUTE(app, "/").methods(crow::HTTPMethod::PUT)([](const crow::request& req)
	{
		// Validate JSON
		auto parsed = crow::json::load(req.body);
		if (!parsed)
		{
			CROW_LOG_WARNING << "[400] 请求体非 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		if (!writeFileAtomically(kDbPath, req.body))
		{
			return jsonCode(500, 1, "写入失败");
		}
		return jsonCode(200, 0);
	});

	// POST / -> merge JSON into existing document (object/object shallow merge)
	CROW_ROUTE(app, "/").methods(crow::HTTPMethod::POST)([](const crow::request& req)
	{
		auto patch = crow::json::load(req.body);
		if (!patch)
		{
			CROW_LOG_WARNING << "[400] 请求体非 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue result;
		if (current.empty())
		{
			result = crow::json::wvalue(patch);
		}
		else
		{
			auto base = crow::json::load(current);
			if (!base)
			{
				// if file corrupted, replace with patch
				result = crow::json::wvalue(patch);
			}
			else
			{
				result = mergeJson(base, patch);
			}
		}
		std::string out = result.dump();
		if (!writeFileAtomically(kDbPath, out))
		{
			return jsonCode(500, 1, "写入失败");
		}
		return jsonCode(200, 0);
	});

	// GET /<resource> -> list or singular
	CROW_ROUTE(app, "/<string>").methods(crow::HTTPMethod::GET)([](const crow::request&, const std::string& resource)
	{
		std::shared_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		if (current.empty())
		{
			return crow::response(200, "{}");
		}
		auto base = crow::json::load(current);
		if (!base || base.t() != crow::json::type::Object)
		{
			return crow::response(200, current);
		}
		for (const auto& kv : base)
		{
			if (kv.key() == resource)
			{
				if (isCollectionObject(kv))
				{
					std::string arr = buildArrayJsonStringFromObjectMap(kv);
					crow::response resp;
					resp.code = 200;
					resp.add_header("Content-Type", "application/json");
					resp.write(arr);
					return resp;
				}
				// singular
				crow::response resp;
				resp.code = 200;
				resp.add_header("Content-Type", "application/json");
				resp.write(crow::json::wvalue(kv).dump());
				return resp;
			}
		}
		return jsonCode(404, 1, "未找到");
	});

	// POST /<resource> -> append to collection or merge/set singular
	CROW_ROUTE(app, "/<string>").methods(crow::HTTPMethod::POST)([](const crow::request& req, const std::string& resource)
	{
		auto incoming = crow::json::load(req.body);
		if (!incoming)
		{
			CROW_LOG_WARNING << "[400] 请求体非 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		crow::json::rvalue base;
		bool hasBase = false;
		if (!current.empty())
		{
			base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				hasBase = true;
				for (const auto& kv : base) root[kv.key()] = kv;
			}
		}
		if (hasBase)
		{
			const crow::json::rvalue* existingPtr = nullptr;
			for (const auto& kv : base)
			{
				if (kv.key() == resource)
				{
					existingPtr = &kv;
					break;
				}
			}
			if (existingPtr && isCollectionObject(*existingPtr))
			{
				// append to collection, compute next id
				int nid = computeNextId(*existingPtr);
				root[resource][std::to_string(nid)] = incoming;
				// ensure id field in stored object
				root[resource][std::to_string(nid)]["id"] = nid;
			}
			else if (existingPtr && existingPtr->t() == crow::json::type::Object && incoming.t() == crow::json::type::Object)
			{
				root[resource] = mergeJson(*existingPtr, incoming);
			}
			else
			{
				root[resource] = incoming;
			}
		}
		else
		{
			root[resource] = incoming;
		}
		std::string out = root.dump();
		if (!writeFileAtomically(kDbPath, out))
		{
			return jsonCode(500, 1, "写入失败");
		}
		return jsonCode(200, 0);
	});

	// PUT /<resource> -> replace singular only
	CROW_ROUTE(app, "/<string>").methods(crow::HTTPMethod::PUT)([](const crow::request& req, const std::string& resource)
	{
		auto incoming = crow::json::load(req.body);
		if (!incoming)
		{
			CROW_LOG_WARNING << "[400] 请求体非 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		if (!current.empty())
		{
			auto base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				for (const auto& kv : base) root[kv.key()] = kv;
			}
		}
		root[resource] = incoming;
		std::string out = root.dump();
		if (!writeFileAtomically(kDbPath, out))
		{
			return jsonCode(500, 1, "写入失败");
		}
		return jsonCode(200, 0);
	});

	// DELETE /<resource> -> remove resource from root (not used for collections)
	CROW_ROUTE(app, "/<string>").methods(crow::HTTPMethod::DELETE)([](const crow::request&, const std::string& resource)
	{
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		bool existed = false;
		bool hasAny = false;
		if (!current.empty())
		{
			auto base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				for (const auto& kv : base)
				{
					if (kv.key() == resource) { existed = true; continue; }
					root[kv.key()] = kv;
					hasAny = true;
				}
			}
		}
		if (!existed)
		{
			return jsonCode(404, 1, "未找到");
		}
		std::string out = hasAny ? root.dump() : std::string("{}");
		if (!writeFileAtomically(kDbPath, out))
		{
			return jsonCode(500, 1, "写入失败");
		}
		return jsonCode(200, 0);
	});

	// Item routes: /<resource>/<int>
	CROW_ROUTE(app, "/<string>/<int>").methods(crow::HTTPMethod::GET)([](const crow::request&, const std::string& resource, int id)
	{
		std::shared_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		if (current.empty()) return jsonCode(404, 1, "未找到");
		auto base = crow::json::load(current);
		if (!base || base.t() != crow::json::type::Object) return jsonCode(404, 1, "未找到");
		for (const auto& kv : base)
		{
			if (kv.key() == resource && isCollectionObject(kv))
			{
				std::string key = std::to_string(id);
				for (const auto& it : kv)
				{
					if (it.key() == key)
					{
						crow::json::wvalue elem(it);
						elem["id"] = id;
						return crow::response(200, elem.dump());
					}
				}
			}
		}
		return jsonCode(404, 1, "未找到");
	});

	CROW_ROUTE(app, "/<string>/<int>").methods(crow::HTTPMethod::PUT)([](const crow::request& req, const std::string& resource, int id)
	{
		auto incoming = crow::json::load(req.body);
		if (!incoming || incoming.t() != crow::json::type::Object)
		{
			CROW_LOG_WARNING << "[400] 请求体非对象 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON 对象");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		bool updated = false;
		crow::json::rvalue base;
		if (!current.empty())
		{
			base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				for (const auto& kv : base) root[kv.key()] = kv;
			}
		}
		std::string idKey = std::to_string(id);
		for (const auto& kv : base)
		{
			if (kv.key() == resource && isCollectionObject(kv))
			{
				root[resource][idKey] = incoming;
				root[resource][idKey]["id"] = id;
				updated = true;
				break;
			}
		}
		if (!updated) return jsonCode(404, 1, "未找到");
		std::string out = root.dump();
		if (!writeFileAtomically(kDbPath, out)) return jsonCode(500, 1, "写入失败");
		return jsonCode(200, 0);
	});

	CROW_ROUTE(app, "/<string>/<int>").methods(crow::HTTPMethod::PATCH)([](const crow::request& req, const std::string& resource, int id)
	{
		auto incoming = crow::json::load(req.body);
		if (!incoming || incoming.t() != crow::json::type::Object)
		{
			CROW_LOG_WARNING << "[400] 请求体非对象 JSON: " << req.body;
			return jsonCode(400, 1, "请求体不是合法 JSON 对象");
		}
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		bool updated = false;
		crow::json::rvalue base;
		if (!current.empty())
		{
			base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				for (const auto& kv : base) root[kv.key()] = kv;
			}
		}
		std::string idKey = std::to_string(id);
		for (const auto& kv : base)
		{
			if (kv.key() == resource && isCollectionObject(kv))
			{
				// merge shallow
				const crow::json::rvalue* existingPtr = nullptr;
				for (const auto& it : kv) { if (it.key() == idKey) { existingPtr = &it; break; } }
				if (!existingPtr) break;
				root[resource][idKey] = mergeJson(*existingPtr, incoming);
				root[resource][idKey]["id"] = id;
				updated = true;
				break;
			}
		}
		if (!updated) return jsonCode(404, 1, "未找到");
		std::string out = root.dump();
		if (!writeFileAtomically(kDbPath, out)) return jsonCode(500, 1, "写入失败");
		return jsonCode(200, 0);
	});

	CROW_ROUTE(app, "/<string>/<int>").methods(crow::HTTPMethod::DELETE)([](const crow::request&, const std::string& resource, int id)
	{
		std::unique_lock<std::shared_mutex> lock(g_dbMutex);
		std::string current = readWholeFile(kDbPath);
		crow::json::wvalue root;
		bool removed = false;
		bool anyLeftInCollection = false;
		crow::json::rvalue base;
		if (!current.empty())
		{
			base = crow::json::load(current);
			if (base && base.t() == crow::json::type::Object)
			{
				for (const auto& kv : base) root[kv.key()] = kv;
			}
		}
		std::string idKey = std::to_string(id);
		for (const auto& kv : base)
		{
			if (kv.key() == resource && isCollectionObject(kv))
			{
				// rebuild collection without the id
				crow::json::wvalue newMap;
				for (const auto& it : kv)
				{
					if (it.key() == idKey) { removed = true; continue; }
					newMap[it.key()] = it;
					anyLeftInCollection = true;
				}
				if (anyLeftInCollection)
				{
					root[resource] = std::move(newMap);
				}
				else
				{
					// remove resource entirely if collection becomes empty
					crow::json::wvalue tmpRoot;
					bool anyTop = false;
					for (const auto& top : base)
					{
						if (top.key() == resource) continue;
						tmpRoot[top.key()] = top;
						anyTop = true;
					}
					root = std::move(tmpRoot);
					// Now root may be empty; handled below when dumping
				}
				break;
			}
		}
		if (!removed) return jsonCode(404, 1, "未找到");
		std::string out;
		if (anyLeftInCollection)
		{
			out = root.dump();
		}
		else
		{
			// collection emptied; if there are other top-level keys besides this resource, root has them
			bool hasOtherTop = false;
			for (const auto& top : base)
			{
				if (top.key() != resource) { hasOtherTop = true; break; }
			}
			out = hasOtherTop ? root.dump() : std::string("{}");
		}
		if (!writeFileAtomically(kDbPath, out)) return jsonCode(500, 1, "写入失败");
		return jsonCode(200, 0);
	});

	// Bind and run
	app.port(8080).concurrency(std::thread::hardware_concurrency()).multithreaded().run();
	return 0;
}


